package html

import java.util.*

  interface Factory<T> {
    fun create() : T
  }

  interface Element

  class TextElement(val text : String) : Element

  abstract class Tag(val name : String) : Element {
    val children = ArrayList<Element>()
    val attributes = HashMap<String, String>()

    protected fun <T : Element> initTag(init :  T.() -> Unit) : T
      {
      val tag = <error descr="[TYPE_PARAMETER_ON_LHS_OF_DOT] Type parameter 'T' cannot have or inherit a companion object, so it cannot be on the left hand side of dot">T</error>.<error descr="[UNRESOLVED_REFERENCE] Unresolved reference: create">create</error>()
      tag.init()
      children.add(tag)
      return tag
    }
  }

  abstract class TagWithText(name : String) : Tag(name) {
    operator fun String.unaryPlus() {
      children.add(TextElement(this))
    }
  }

  class HTML() : TagWithText("html") {
    companion object : Factory<HTML> {
      override fun create() = HTML()
    }

    fun head(init :  Head.() -> Unit) = initTag<Head>(init)

    fun body(init :  Body.() -> Unit) = initTag<Body>(init)
  }

  class Head() : TagWithText("head") {
    companion object : Factory<Head> {
      override fun create() = Head()
    }

    fun title(init :  Title.() -> Unit) = initTag<Title>(init)
  }

  class Title() : TagWithText("title")

  abstract class BodyTag(name : String) : TagWithText(name) {
  }

  class Body() : BodyTag("body") {
    companion object : Factory<Body> {
      override fun create() = Body()
    }

    fun b(init :  B.() -> Unit) = initTag<B>(init)
    fun p(init :  P.() -> Unit) = initTag<P>(init)
    fun h1(init :  H1.() -> Unit) = initTag<H1>(init)
    fun a(href : String, init :  A.() -> Unit) {
      val a = initTag<A>(init)
      a.href = href
    }
  }

  class B() : BodyTag("b")
  class P() : BodyTag("p")
  class H1() : BodyTag("h1")
  class A() : BodyTag("a") {
    var href : String?
      get() = attributes["href"]
      set(value) {
        if (value != null)
        attributes["href"] = value
      }
  }

  operator fun MutableMap<String, String>.set(key : String, value : String) = this.put(key, value)

  fun html(init :  HTML.() -> Unit) : HTML {
    val html = HTML()
    html.init()
    return html
  }

fun result(args : Array<String>) =
  html {
    head {
      title {+"XML encoding with Groovy"}
    }
    body {
      h1 {+"XML encoding with Groovy"}
      p {+"this format can be used as an alternative markup to XML"}

      // an element with attributes and text content
      a(href = "https://groovy.codehaus.org") {+"Groovy"}

      // mixed content
      p {
        +"This is some"
        b {+"mixed"}
        +"text. For more see the"
        a(href = "https://groovy.codehaus.org") {+"Groovy"}
        +"project"
      }
      p {+"some text"}

      // content generated by
      p {
        for (arg in args)
          +arg
      }
    }
  }
